﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ClearingRestartingAutomaton.Common;

namespace ClearingRestartingAutomaton.Base
{
    /// <summary>
    /// The structure InstructionStep describes the application of the specified instruction 
    /// to the given input word. Only one of the two possible ways how to apply the instruction is used.
    /// </summary>
    [Serializable]
    public struct InstructionStep
    {
        #region Constructors

        /// <summary>
        /// Constructor of this structure.
        /// </summary>
        /// <param name="instruction">The instruction that was applied to the given input word.</param>
        /// <param name="use">The way the instruction was used.</param>
        /// <param name="word">The word to which the instruction was applied.</param>
        /// <param name="startIndex">The starting position of the subword to which the instruction was applied.</param>
        /// <param name="length">The length of the subword to which the instruction was applied.</param>
        /// <param name="resultWord">The word obtained from the given input word after the application of the instruction.</param>
        public InstructionStep(
            Instruction instruction,
            InstructionUse use,
            string word,
            int startIndex,
            int length,
            string resultWord)
        {
            this.intruction = instruction;
            this.use = use;
            this.word = word;
            this.startIndex = startIndex;
            this.length = length;
            this.resultWord = resultWord;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// The instruction that was applied to the given input word.
        /// </summary>
        public Instruction Instruction
        {
            get { return this.intruction; }
        }

        /// <summary>
        /// The way the instruction was used.
        /// </summary>
        public InstructionUse Use
        {
            get { return this.use; }
        }

        /// <summary>
        /// The word to which the instruction was applied.
        /// </summary>
        public string Word
        {
            get { return this.word; }
        }

        /// <summary>
        /// The starting position of the subword to which the instruction was applied.
        /// </summary>
        public int StartIndex
        {
            get { return this.startIndex; }
        }

        /// <summary>
        /// The length of the subword to which the instruction was applied.
        /// </summary>
        public int Length
        {
            get { return this.length; }
        }

        /// <summary>
        /// The word obtained from the given input word after the application of the instruction.
        /// </summary>
        public string ResultWord
        {
            get { return this.resultWord; }
        }

        /// <summary>
        /// The left context of the subword to which the instruction was applied.
        /// </summary>
        public string Left
        {
            get { return this.word.Substring(0, this.startIndex); }
        }

        /// <summary>
        /// The subword to which the instruction was applied.
        /// </summary>
        public string Center
        {
            get
            {
                switch (this.use)
                {
                    case InstructionUse.Reduction:
                        return this.intruction.FromWord;
                    case InstructionUse.Generation:
                        return this.intruction.ToWord;
                    default:
                        throw new InvalidProgramException("Unknown instruction use.");
                }
            }
        }

        /// <summary>
        /// The resulting subword obtained from the subword after the application of the instruction.
        /// </summary>
        public string CenterRewritten
        {
            get
            {
                switch (this.use)
                {
                    case InstructionUse.Reduction:
                        return this.intruction.ToWord;
                    case InstructionUse.Generation:
                        return this.intruction.FromWord;
                    default:
                        throw new InvalidProgramException("Unknown instruction use.");
                }
            }
        }

        /// <summary>
        /// The right context of the subword to which the instruction was applied.
        /// </summary>
        public string Right
        {
            get { return this.word.Substring(this.startIndex + this.length); }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns a System.String that represents this instruction step.
        /// </summary>
        /// <returns>A System.String that represents this instruction step.</returns>
        public override string ToString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(this.Left);
            stringBuilder.Append("[");
            stringBuilder.Append(this.Center);
            stringBuilder.Append("]");
            stringBuilder.Append(this.Right);
            stringBuilder.Append(" ");
            switch (this.use)
            {
                case InstructionUse.Reduction:
                    stringBuilder.Append(StringDefaultValues.ReductionArrow); break;
                case InstructionUse.Generation:
                    stringBuilder.Append(StringDefaultValues.GeneratingArrow); break;
                default:
                    throw new InvalidProgramException("Unknown instruction use.");
            }
            stringBuilder.Append(" ");
            stringBuilder.Append(this.ResultWord);
            return stringBuilder.ToString();
        }

        #endregion

        #region Private Data Members

        private Instruction intruction;
        private InstructionUse use;
        private string word;
        private int startIndex;
        private int length;

        private string resultWord;

        #endregion
    }
}
